﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Trippy.Animation;
using Trippy.Abilities;
using Trippy.CollidableGameComponents;
using Trippy.CollidableGameComponents.Objects;
using Trippy.Levels;
using System.Linq;

namespace Trippy.CollidableGameComponents.Agents
{

    public class Hero : Agent
    {
        public bool Frozen { get; set; }

        public static Texture2D SpriteSheet { get; private set; }

        protected override void LoadContent()
        {
            if (SpriteSheet == null) SpriteSheet = Game.Content.Load<Texture2D>("Sprites/Agents/hero_sheet_revised");
            Sprite = new AnimatedSprite(this, SpriteSheet, 68, 40, 0, 0, -6, -11, 4, 0.1f);
            base.LoadContent();
        }

        public override void Draw(GameTime gameTime)
        {
            if(State != HeroState.Dying
                || Math.Floor(DeathTimer * 4f) % 2 == 1)
                StandardDraw(gameTime);
            base.Draw(gameTime);
        }

        public enum HeroState { None, Grounded, Jumping, Dying }

        protected HeroState state;
        public HeroState State 
        {
            get
            {
                return state;
            }
            protected set
            {
                if (state != HeroState.Dying //so nothing can prevent death once you start dying. :(
                     || DeathTimer <= 0f)    //ah, the inevitability.
                    state = value;
            }
        }
        static public Ability PreviousAbility;
        static protected Ability currentAbility;
        public Ability CurrentAbility
        {
            get
            {
                return currentAbility;
            }
            set
            {
                if (currentAbility != null)
                    currentAbility.OnRemove();
                PreviousAbility = currentAbility;
                currentAbility = value;
                if(currentAbility != null)
                     currentAbility.OnAdd();
            }
        }

        static public int Lives;
        static protected int score;
        static public int Score
        {
            get
            {
                return score;
            }
            set
            {
                score = value;
                if (score > 50)
                {
                    score -= 50;
                    Lives++;
                }
            }
        }

        public Hero(Level level)
            : base(level)
        {
            Frozen = false;
            CurrentAbility = null;
            PreviousAbility = null;
            //Lives = TrippyConstants.Agent.Hero.Lives;
            State = HeroState.Grounded;
            JumpVelocityVector = TrippyConstants.Agent.Jump;
            BounceVelocityVector = TrippyConstants.Agent.Bounce;
            //Score = 0;
            TimesDied = new Dictionary<MannerOfDeath, int>();
        }

        public void RemoveAbility()
        {
            PreviousAbility = null; //reset them entirely for this function.
            CurrentAbility = null;
        }

        public void RestoreAbility()
        {
            CurrentAbility = PreviousAbility;
            PreviousAbility = null; //now abilities that restore others are just dumped off the queue.
        }

        public override void MarkForDeletion(MannerOfDeath d, float dt = 0f)
        {
            Die(d);
        }

        public enum MannerOfDeath { Falling, Enemy, Obstacles, Suicide, NotApplicable }
        protected Dictionary<MannerOfDeath, int> TimesDied;

        protected float DeathTimer;
        protected MannerOfDeath CurrentMoD;

        protected void SetDeathTimer(MannerOfDeath d) //OH NO PLAYER IS DYINGGGGG
        {
            CurrentMoD = d;
            DeathTimer = 2.0f;
            State = HeroState.Dying;
        }

        protected void Dead(MannerOfDeath d) //player has, alas, finally died.
        {
            if (!TimesDied.ContainsKey(d))
                TimesDied[d] = 1;
            else
                TimesDied[d]++;
            switch (d)
            {
                case MannerOfDeath.Falling:
                    if (TimesDied[d] > 8)
                        CurrentAbility = new Fly(CurrentLevel);
                    else if (TimesDied[d] > 5)
                        CurrentAbility = new Jump(CurrentLevel);
                    else if (TimesDied[d] > 2)
                        CurrentAbility = new Sprint(CurrentLevel);
                    else
                        CurrentAbility = new OneTimeJump(CurrentLevel);
                    break;
                case MannerOfDeath.Obstacles:
                    if (TimesDied[d] > 8)
                        CurrentAbility = new Invincibility(CurrentLevel, 30.0f);
                    else if (TimesDied[d] > 5)
                        CurrentAbility = new ActivateableInvincibility(CurrentLevel, 10.0f);
                    else if (TimesDied[d] > 2)
                        CurrentAbility = new PassiveInvincibility(CurrentLevel, 5.0f);
                    else
                        CurrentAbility = new Grow(CurrentLevel);
                    break;
                case MannerOfDeath.Enemy:
                    if (TimesDied[d] > 8)
                        CurrentAbility = new Firestorm(CurrentLevel);
                    else if (TimesDied[d] > 5)
                        CurrentAbility = new Rocket(CurrentLevel);
                    else 
                        CurrentAbility = new LimitedRockets(CurrentLevel, TimesDied[d]);
                    break;
            }
            Lives--;
            if (Lives == 0)
            {
                CurrentLevel.Game.rainLoop.Stop();
                var gameOver = new UI_Overlay.GameOver(CurrentLevel.Game, CurrentLevel);
                Game.Components.Clear();
                Game.Components.Add(gameOver);
            }
            else
            {
                CurrentLevel.Camera.InstantZoom(2f);
                CurrentLevel.Camera.LinearZoom(1f, .75f);
                foreach (CollidableGameComponent element in CurrentLevel.powerList)
                {
                    if(!CurrentLevel.Objects.Contains(element))
                        element.Restore();
                    element.MarkForAddition();
                }
                CurrentLevel.powerList.Clear();
                CurrentLevel.Spawn(this);
            }
        }
 
        public void Die(MannerOfDeath d)
        {
            if (((CurrentAbility != null
                && CurrentAbility.OnDie(d))
                || CurrentAbility == null) //Ability might be able to prevent the death.
                && State != HeroState.Dying) //no dying twice.
            {
                RemoveAbility();
                SetDeathTimer(d);                
            }
        }

        public void AddPoints(int points)
        {
            Score += points;
        }

        private bool onGround;
        private bool prevGround = false;
        public override void PreUpdate()
        {
             onGround = false;
             base.PreUpdate();
        }

        protected override void CollisionHandler(CollidableGameComponent component, CollidableGameComponent.CollisionSide side)
        {
            if (CurrentAbility != null)
                CurrentAbility.OnCollide(component, side);

            if (component is PowerUp)
            {
                PowerUp powerUp = component as PowerUp;
                if (CurrentAbility == null
                    || (CurrentAbility.GetType() != powerUp.Ability.GetType()))
                {
                    CurrentAbility = powerUp.Ability;
                }
            }
            base.CollisionHandler(component, side);
        }

        public override void PostUpdate(float elapsedTime)
        {
            if (onGround == false)
            {
                State = HeroState.Jumping;
                Sprite.CurrentFrame = 1;
                if (prevGround == true && CurrentAbility != null)
                    CurrentAbility.OnAir();
            }
            prevGround = onGround;
            base.PostUpdate(elapsedTime);
        }

        public override void HitGround(float y, Vector2 vel = new Vector2())
        {
            onGround = true;
            State = HeroState.Grounded;
            if (Velocity.Y > 50f)
            {
                if (CurrentLevel.CurrentKeyboardState.IsKeyUp(CurrentLevel.Game.left)
                && CurrentLevel.CurrentKeyboardState.IsKeyUp(CurrentLevel.Game.right))
                {
                    Velocity = new Vector2(0f, 0f);
                    Sprite.CurrentFrame = 0;
                }
                else
                {
                    Velocity = new Vector2(Velocity.X * 0.5f, Velocity.Y);
                }
            }
            base.HitGround(y);
            if (vel != new Vector2())
            {
                Velocity = vel;
            }
            if (CurrentAbility != null)
                CurrentAbility.OnLand();
        }

        public override void Bounce(float y)
        {
            State = HeroState.Jumping;
            base.Bounce(y);
        }


        public override void Update(GameTime gameTime)
        {
            float elapsedTime = TrippyUtilities.CalculateElapsedTime(gameTime);

            KeyboardState k = CurrentLevel.CurrentKeyboardState;
            KeyboardState pk = CurrentLevel.PreviousKeyboardState;
            ProcessKeyboardState(k, elapsedTime, pk);

            if (State == HeroState.Dying)
            {
                DeathTimer -= elapsedTime;
                if (DeathTimer <= 0) //your time is up
                    Dead(CurrentMoD);
            }
            else if (!Frozen)
                UpdateStandardPhysics(elapsedTime);

            base.Update(gameTime);
        }

        private void ProcessKeyboardState(KeyboardState k, float elapsedTime, KeyboardState pk)
        {
            if (Frozen) return;
           

            //note: do not consolidate CurrentAbility != null checks in case one of these events removes the CurrentAbility
            if (CurrentAbility != null)
                CurrentAbility.OnUpdate(elapsedTime);

            if ((CurrentAbility != null)
            && k.IsKeyDown(CurrentLevel.Game.action) && pk.IsKeyUp(CurrentLevel.Game.action) 
            && (CurrentAbility.State == Ability.AbilityState.Available))
                CurrentAbility.OnActivate();
            else if ((CurrentAbility != null) //since we don't really want activate -> cancel on same button press...
            && k.IsKeyDown(CurrentLevel.Game.action) && pk.IsKeyUp(CurrentLevel.Game.action)
            && (CurrentAbility.State == Ability.AbilityState.Active))
                CurrentAbility.OnCancel();

            if (k.IsKeyDown(CurrentLevel.Game.action)
                && (CurrentAbility != null))
                CurrentAbility.OnHold(elapsedTime);

            if (k.IsKeyUp(CurrentLevel.Game.action) && pk.IsKeyDown(CurrentLevel.Game.action)
                && (CurrentAbility != null))
                CurrentAbility.OnRelease();

            if (((k.IsKeyDown(CurrentLevel.Game.jump) && pk.IsKeyUp(CurrentLevel.Game.jump)))
                && (CurrentAbility != null))
                CurrentAbility.OnJump();

            if (k.IsKeyDown(CurrentLevel.Game.left)
                && (CurrentAbility != null))
                CurrentAbility.OnMoveLeft(elapsedTime);

            if (k.IsKeyDown(CurrentLevel.Game.right)
                && (CurrentAbility != null))
                CurrentAbility.OnMoveRight(elapsedTime);

            //menu business
            if (k.IsKeyDown(CurrentLevel.Game.menu))
            {
                CurrentLevel.Game.rainLoop.Stop();
                var inGamePause = new UI_Overlay.InGamePause(CurrentLevel.Game, CurrentLevel);
                Game.Components.Clear();
                Game.Components.Add(inGamePause);
            }

            if (State == HeroState.Grounded)
            {
                if (k.IsKeyDown(CurrentLevel.Game.right))
                {
                    Sprite.ChangeAnimation(1);
                    Sprite.Step();
                    Velocity = new Vector2(Velocity.X + elapsedTime * 400.0f, Velocity.Y);
                }
                else if (k.IsKeyDown(CurrentLevel.Game.left))
                {
                    Sprite.ChangeAnimation(0);
                    Sprite.Step();
                    Velocity = new Vector2(Velocity.X - elapsedTime * 400.0f, Velocity.Y);
                }
                if (k.IsKeyDown(CurrentLevel.Game.jump))
                {
                    Jump();
                }
                if (k.IsKeyUp(CurrentLevel.Game.right) && pk.IsKeyDown(CurrentLevel.Game.right))
                {
                    if (Velocity.X > 0)
                    {
                        Velocity = new Vector2(0f, Velocity.Y);
                        //if (Velocity.X < 200)
                        //    Velocity = new Vector2(0f, Velocity.Y);
                        //else
                        //    Velocity = new Vector2(Velocity.X * 0.1f, Velocity.Y);
                    }
                }
                if (k.IsKeyUp(CurrentLevel.Game.left) && pk.IsKeyDown(CurrentLevel.Game.left))
                {
                    Velocity = new Vector2(0f, Velocity.Y);
                    //if (Velocity.X < 0)
                    //    if (Velocity.X > -200)
                    //        Velocity = new Vector2(0f, Velocity.Y);
                    //    else
                    //        Velocity = new Vector2(Velocity.X * 0.1f, Velocity.Y);
                }
            }
            else if (State == HeroState.Jumping)
            {
                if (k.IsKeyDown(CurrentLevel.Game.right))
                {
                    Sprite.ChangeAnimation(1);
                    if (pk.IsKeyDown(CurrentLevel.Game.left)) Velocity = new Vector2(Velocity.X * 0.1f, Velocity.Y);
                    Velocity = new Vector2(Velocity.X + elapsedTime * 285.0f, Velocity.Y);
                }
                if (k.IsKeyDown(CurrentLevel.Game.left))
                {
                    Sprite.ChangeAnimation(0);
                    if (pk.IsKeyDown(CurrentLevel.Game.right)) Velocity = new Vector2(Velocity.X * 0.1f, Velocity.Y);
                    Velocity = new Vector2(Velocity.X - elapsedTime * 285.0f, Velocity.Y);
                }
            }
            if ((k.IsKeyUp(CurrentLevel.Game.right) && pk.IsKeyDown(CurrentLevel.Game.right))
                || (k.IsKeyUp(Keys.Left) && pk.IsKeyDown(Keys.Left)))
            {
               
                Sprite.CurrentFrame = 0;
            }

        }

        public override void Jump()
        {
            State = HeroState.Jumping;
            Sprite.CurrentFrame = 1;
            new EffectAnimation(CurrentLevel, EffectAnimation.Effects.SmokeJump, Position);
            base.Jump();
        }

        public override void Restore()
        {
            state = HeroState.Jumping; //lowercase state bypasses State restrictions on death
            Frozen = false;
            if(CurrentAbility != null)
                CurrentAbility.OnRemove();
        }

    }
}
